package at.codebase.web.servlets.item;

import static at.codebase.web.util.i18n.I18NFactory.i18n;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

import at.codebase.core.LinkCreator;
import at.codebase.core.RichtTextFormatter;
import at.codebase.db.dao.EItemDao;
import at.codebase.db.dao.EItemFeedbackDao;
import at.codebase.db.entities.EImage;
import at.codebase.db.entities.EItem;
import at.codebase.db.entities.EItemFeedback;
import at.codebase.db.entities.EItemFeedback.EItemFeedbackRating;
import at.codebase.db.entities.EUser;
import at.codebase.security.SecureStringBuilder;
import at.codebase.security.SecurityUtils;
import at.codebase.web.servlets.item.ItemInputStatus.InputStatus;
import at.codebase.web.servlets.item.ItemInputStatus.InputStatusFeedbackRating;
import at.codebase.web.servlets.item.ItemInputStatus.InputStatusFeedbackText;
import at.codebase.web.servlets.item.ItemInputStatus.InputStatusVoid;
import at.codebase.web.templates.ErrorWidget;
import at.codebase.web.templates.ItemDetailsFeedback;
import at.codebase.web.templates.MainTemplate;
import at.codebase.web.templates.RubyRatingBar;
import at.codebase.web.util.LocalRequest.RequestInformation;
import at.codebase.web.util.URLParameters.ParameterException;
import at.codebase.web.util.i10n.L10N;
import at.codebase.web.util.servlet.SecureServlet;
import at.codebase.web.util.template.Template;
import at.codebase.web.util.template.TemplateEngine;

import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.googlecode.objectify.Key;

/**
 * The main site of an item. Principle visible to all users.
 * 
 * @author Lukas Pfeifhofer <lukas.pfeifhofer@devlabs.pro>
 */
@SuppressWarnings("rawtypes")
public class ItemDetails extends SecureServlet {
	
	private static final int WaitTimeMs = 100;
	private static final int MaxWaitTimeMs = 1500;
	static HashMap<String, InputStatus> emptyInputStatusMap = new HashMap<String, InputStatus>();

	static {
		emptyInputStatusMap.put("feedbackRating", new InputStatusVoid());
		emptyInputStatusMap.put("feedbackText", new InputStatusVoid());
	}
	
	private class FeedbackData {
		boolean userAlreadyRated;
		List<EItemFeedback> itemFeedbackList;
		double ratingAverage;
		int ratingTotal;
	}
	
	@Override
	protected void initSecurityLevels(ArrayList<UserLevel> secLevels) {
		secLevels.add(UserLevel.ANONYMOUS);
		secLevels.add(UserLevel.REGISTERED);
		secLevels.add(UserLevel.ADMIN);
	}
	
	@Override
	protected RequestType requestType() {
		return RequestType.POST_AND_GET;
	}

	@Override
	protected void processRequestSecurityLevel(
			RequestInformation requestInfo, UserLevel level,
			RequestType requestType) {
		EItem item = null;
		int sumWaited = 0;
		while(item == null && sumWaited < MaxWaitTimeMs) {
			try {
				long id = requestInfo.getParameters().getLong("itemId");
				item = EItemDao.getItemById(new Long(id));
				if(item == null) {
					Thread.sleep(WaitTimeMs);
					sumWaited += WaitTimeMs;
				}
			} catch (ParameterException e) {	
				//TODO: Invalid id, maybe log that?
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
			}
		}
		
		MainTemplate mainTemplate = new MainTemplate(requestInfo, writer());
		
		mainTemplate.addJSScript("script_item_details.js");
		
		if(item != null){
			EUser userSeller = item.getSeller().get();
			//Check if Item is active
			if (item.isActive() || !item.isExpired()
					//If user is Admin
					|| level.equals(UserLevel.ADMIN)
					//If user is logged and seller of the Item
					|| (requestInfo.isUserLoggedIn() && item
							.checkSeller(requestInfo.getUser().getId()))) {
				mainTemplate.setTitle(i18n(requestInfo.getLocale()).string(
						item.getTitle()));
				
				//Feedback
				FeedbackData feedbackData = new FeedbackData();
				feedbackData.userAlreadyRated = false;
				feedbackData.itemFeedbackList = EItemFeedbackDao.getFeedbackByItem(item.getId());
				double ratingSum = 0;
				for(EItemFeedback itemFeedback:feedbackData.itemFeedbackList){
					if(itemFeedback != null){
						if(requestInfo.isUserLoggedIn() && itemFeedback.getAuthor().get().getId().equals(requestInfo.getUser().getId())){
							feedbackData.userAlreadyRated = true;
						}
						ratingSum += itemFeedback.getFeedbackRatingValue();
						feedbackData.ratingTotal++;
					}
				}
				feedbackData.ratingAverage = ratingSum / ((double)feedbackData.ratingTotal);

				
				if(requestInfo.isUserLoggedIn() && !feedbackData.userAlreadyRated && !userSeller.getId().equals(requestInfo.getUser().getId())){
					if(requestType.equals(RequestType.POST)){
						if(!tokenCorrect(requestInfo)) {
							processRequestNoAccess(requestInfo, level);
							return;
						}
						
						HashMap<String, InputStatus> inputStatusMap = new HashMap<String, InputStatus>();
						
						try {
							inputStatusMap.put("feedbackRating", new InputStatusFeedbackRating(requestInfo.getParameters().getString("feedbackRating")));
						} catch (ParameterException e) {
							inputStatusMap.put("feedbackRating", new InputStatusFeedbackRating(null));
						}
						
						try {
							inputStatusMap.put("feedbackText", new InputStatusFeedbackText(requestInfo.getParameters().getString("feedbackText")));
						} catch (ParameterException e) {
							inputStatusMap.put("feedbackText", new InputStatusFeedbackText(null));
						}
						
						boolean allValid = true;
						
						for(String key:inputStatusMap.keySet()){
							InputStatus inputStatus = inputStatusMap.get(key);
							inputStatus.compile();
							if(inputStatus.isError())
								allValid = false;
						}
						
						
						if(allValid){
							//Save Feedback
							EItemFeedbackRating feedbackRating = ((InputStatusFeedbackRating) inputStatusMap.get("feedbackRating")).getValue();
							String feedbackText = ((InputStatusFeedbackText) inputStatusMap.get("feedbackText")).getValue();
							EItemFeedback itemFeedback = EItemFeedback.create(item.getId(), requestInfo.getUser().getId(), feedbackRating, feedbackText);
							
							EItemFeedbackDao.addItemFeedback(itemFeedback);
	
							redirectTo("/item/"+item.getId());
						}else{
							// Show the Item Details
							buildPage(mainTemplate, requestInfo, level, item, inputStatusMap, feedbackData);
						}
					}else{
						// Show the Item Details
						buildPage(mainTemplate, requestInfo, level, item, emptyInputStatusMap, feedbackData);
					}
				}else{
					// Show the Item Details
					buildPage(mainTemplate, requestInfo, level, item, emptyInputStatusMap, feedbackData);	
				}
			}else{
				//Show Not Found Error (Because Item is not active)
				show404NotFound(mainTemplate, requestInfo);
			}
		}else{
			//Show Not Found Error
			show404NotFound(mainTemplate, requestInfo);
		}
		
		mainTemplate.render(requestInfo.getLocale());
		
	}
	
	private void buildPage(MainTemplate mainTemplate, RequestInformation requestInfo, UserLevel level, EItem item, HashMap<String, InputStatus> inputStatusMap, FeedbackData feedbackData){
		EUser userSeller = item.getSeller().get();
		
		Template itemDetails = TemplateEngine.getTemplate("item/site_details_item.html");
		
		//Load Images
		ArrayList<Key<EImage>> imageKeys = item.getImageKeys();
		SecureStringBuilder imageListString = new SecureStringBuilder();
		if(imageKeys.size()>0){
			int c = 0;
			for(Key<EImage> imageKey:imageKeys){
				imageListString.appendInsecure("<img id=\"imageId-"+c+"\"" +
						((c!=0)?"style=\"display:none;\"":"")
						+ " class=\"itemDetailsImage\" src=\"/service/image?id="+imageKey.getId()+"&w=195&h=195"+"\">");
				c++;
			}
		}else{
			imageListString.appendInsecure("<img class=\"itemDetailsImage\" src=\"/images/no_image_available.png\">");
		}
		imageListString.appendInsecure("<script>ItemDetails.maxImage = "+imageKeys.size()+";</script>");
		itemDetails.setHTML("imageList", imageListString.toString());
		itemDetails.setText("imagesTotal", String.valueOf(imageKeys.size()));
		
		itemDetails.setText("itemId", String.valueOf(item.getId()));
		itemDetails.setText("itemTitle", item.getTitle());
		itemDetails.setHTML(
				"itemTimeCreate",
				i18n(requestInfo.getLocale()).string(
						"itemDetails_createTime",
						L10N.formatDate(requestInfo.getLocale(), item.getTimeCreate()),
						L10N.formatTime(requestInfo.getLocale(), item.getTimeCreate())));

		if(requestInfo.isUserLoggedIn() && item.checkSeller(requestInfo.getUser().getId())){
			itemDetails.setHTML(
					"itemTimeExpire",
					i18n(requestInfo.getLocale()).string(
							"itemDetails_expireTime",
							L10N.formatDate(requestInfo.getLocale(), item.getTimeExpiration()),
							L10N.formatTime(requestInfo.getLocale(), item.getTimeExpiration())));
		}
		itemDetails.setHTML(
				"itemPrice",
				i18n(requestInfo.getLocale()).string(
						"itemDetails_priceLabel",
						L10N.formatMoney(requestInfo.getLocale(), item.getPrice())));
		
		String safeDescriptionText =  SecurityUtils.sanatize(item.getDescription());
		itemDetails.setHTML("itemDescription", RichtTextFormatter.toRichText(safeDescriptionText).replace("\n", "<br>"));
		itemDetails.setHTML("itemSeller", LinkCreator.userLink(userSeller.getId(), userSeller.getUsername()));
		
		itemDetails.setTemplate("itemRatingRubyBar", new RubyRatingBar(feedbackData.ratingAverage, feedbackData.ratingTotal));
		
		//Feedback
		ArrayList<Template> feedbackTemplates = new ArrayList<Template>();
	
		Comparator<EItemFeedback> newestFirstComparator = new Comparator<EItemFeedback>() {
			@Override
			public int compare(EItemFeedback o1, EItemFeedback o2) {
				if(o1.getTimeCreate() < o2.getTimeCreate()){
					return 1;
				}else if(o1.getTimeCreate() > o2.getTimeCreate()){
					
					return -1;
				}else{
					return 0;
				}
			}
		};
		Collections.sort(feedbackData.itemFeedbackList, newestFirstComparator);
		for(EItemFeedback itemFeedback:feedbackData.itemFeedbackList){
			if(itemFeedback != null){
				ItemDetailsFeedback feedbackContainer = new ItemDetailsFeedback(requestInfo, level, itemFeedback);
				feedbackTemplates.add(feedbackContainer);
			}
		}
		if(feedbackTemplates.size()>0){
			itemDetails.setTemplates("itemFeedbackList", feedbackTemplates);
		}else{
			itemDetails.setHTML("itemFeedbackList", "<div class=\"itemDetailsFeedbackNothing\">"+i18n(requestInfo.getLocale()).string("itemDetails_feedback_nothing")+"</div>");
		}
		/*
		 * Global
		 */

		if(!item.isActive()){
			itemDetails.setHTML(
					"dectivatedFrame",
					"<div class=\"itemDetailsDectivatedFrame\">"
							+ i18n(requestInfo.getLocale()).string(
									"itemDetails_info_notActive") + "</div>");
		}
		
		if(item.isExpired()){
			itemDetails.setHTML(
					"expiredFrame",
					"<div class=\"itemDetailsExpiredFrame\">"
							+ i18n(requestInfo.getLocale()).string(
									"itemDetails_info_expired") + "</div>");
		}
		
		/*
		 * Admin Menu
		 */
		if(level.equals(UserLevel.ADMIN)){
			Template adminMenu = TemplateEngine.getTemplate("item/site_details_menu_admin.html");
			adminMenu.setText("randomKey", nextToken());
			adminMenu.setText("itemId", String.valueOf(item.getId()));
			if(!item.isActive()){
				adminMenu.setText("activateToggle", "activate");
				adminMenu.setText("activateToggleText", i18n(requestInfo.getLocale()).string("itemDetails_menu_activate"));
			}else{
				adminMenu.setText("activateToggle", "deactivate");
				adminMenu.setText("activateToggleText", i18n(requestInfo.getLocale()).string("itemDetails_menu_deactivate"));
			}
			itemDetails.setTemplate("itemAdminMenu", adminMenu);
		}
		if(requestInfo.isUserLoggedIn()){
			/*
			 * Seller Menu
			 */
			if(item.checkSeller(requestInfo.getUser().getId())){
				Template sellerMenu = TemplateEngine.getTemplate("item/site_details_menu_seller.html");
				sellerMenu.setText("itemId", String.valueOf(item.getId()));
				if(!item.isActive()){
					sellerMenu.setText("activateToggle", "activate");
					sellerMenu.setText("activateToggleText", i18n(requestInfo.getLocale()).string("itemDetails_menu_activate"));
				}else{
					sellerMenu.setText("activateToggle", "deactivate");
					sellerMenu.setText("activateToggleText", i18n(requestInfo.getLocale()).string("itemDetails_menu_deactivate"));
				}
				itemDetails.setTemplate("itemSellerMenu", sellerMenu);
				
				itemDetails.setHTML("giveFeedback", "<div class=\"itemDetailsGiveFeedbackOwnItem\">"+i18n(requestInfo.getLocale()).string("itemDetails_giveFeedback_ownItem")+"<div>");
			}else{
			/*
			 * Customer Menu
			 */
				Template customerMenu = TemplateEngine.getTemplate("item/site_details_menu_customer.html");
				customerMenu.setText("sellerId", String.valueOf(userSeller.getId()));
				customerMenu.setText("itemId", String.valueOf(item.getId()));
				itemDetails.setTemplate("itemCustomerMenu", customerMenu);
				
				//Costumer Feedback
				Template giveFeedbackT = TemplateEngine.getTemplate("item/site_details_give_feedback.html");	
				giveFeedbackT.setText("randomKey", nextToken());
				giveFeedbackT.setText("itemId", String.valueOf(item.getId()));
				
				//ErrorWidget
				ErrorWidget errorWidget = new ErrorWidget();
				if(inputStatusMap.get("feedbackRating").isError())
					errorWidget.addErrorItem(i18n(requestInfo.getLocale()).string("itemDetails_giveFeedback_errorRating"));
				if(inputStatusMap.get("feedbackText").isError())
					errorWidget.addErrorItem(i18n(requestInfo.getLocale()).string("itemDetails_giveFeedback_errorText"));
				
				if(errorWidget.hasErrors())
					giveFeedbackT.setTemplate("errorWidget", errorWidget);
				
				giveFeedbackT.setHTML("feedbackRatingValue", new SecureStringBuilder().appendSecure(inputStatusMap.get("feedbackRating").getRawString()).toString());
				giveFeedbackT.setHTML("feedbackTextValue", new SecureStringBuilder().appendSecure(inputStatusMap.get("feedbackText").getRawString()).toString());

				if(!feedbackData.userAlreadyRated){
					itemDetails.setTemplate("giveFeedback", giveFeedbackT);
				}else{
					itemDetails.setHTML("giveFeedback", "<div class=\"itemDetailsGiveFeedbackAlreadyRated\">"+i18n(requestInfo.getLocale()).string("itemDetails_giveFeedback_alreadyRated")+"</div>");
				}
				
			}
		}else{
			Template anonCustomerMenu = TemplateEngine.getTemplate("item/site_details_menu_anonymous.html");
			UserService userService = UserServiceFactory.getUserService();
			anonCustomerMenu.setHTML("loginUrl", userService.createLoginURL("/user/profile"));
			itemDetails.setTemplate("itemCustomerMenu", anonCustomerMenu);
		}

		if(item.getLocation() != null) {
			//Item Details
			itemDetails.setText("locationLatitude", String.valueOf(item.getLatitude()));
			itemDetails.setText("locationLongitude", String.valueOf(item.getLongitude()));
		} else {
			itemDetails.setText("googleMap", "");
		}
		mainTemplate.setTemplate("body", itemDetails);
	}
	
	private void show404NotFound(MainTemplate mainTemplate, RequestInformation requestInfo){
		mainTemplate.setTitle(i18n(requestInfo.getLocale()).string("itemDetails_error_notFound_title"));
		Template notFoundItemTemplate = TemplateEngine.getTemplate("item/error_not_found_item.html");
		mainTemplate.setTemplate("body", notFoundItemTemplate);
	}

	@Override
	protected void processRequestNoAccess(RequestInformation requestInfo,
			UserLevel level) {

	}

}
